package iniReader

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"time"
	"errors"
)

type Pair map[string]string

// ini 文件结构
type Config struct{
	fileName string
	// key-value 对
	configuration []map[string]Pair
}

// 定义 listen 接口
type Listener interface {
	Listen(inifile string)
}

// 定义接口函数
type ListenFunc func(iniFile string) (*Config, error)

// 接口函数
func (l ListenFunc) Listen(iniFile string) (*Config, error) {
	return l(iniFile)
}

var comment string

// 不同操作系统使用不同的的注释行
func Init() {
	sysType := runtime.GOOS
	if sysType == "linux" {
		comment = "#"
	} 
 	if sysType == "windows" {
		comment = ";"
	}
}

// 初始化 Config
func initConfig(fileName string) *Config {
	conf := new(Config)
	conf.fileName = fileName;
	return conf;
}

// 读 ini 文件
func (c *Config) ReadIni() ([]map[string]Pair, error) {
	var e error = nil;
	file, err := os.Open(c.fileName)
	if err != nil {
		e = errors.New("[Error] Failed to open file!")
		return nil, e
	}
	defer file.Close()
	var data map[string]Pair
	buf := bufio.NewReader(file)
	section := ""
	
	for {
		// 读入一行
		l, _, err := buf.ReadLine()
		if err == io.EOF {
			break;
		}
		if err != nil{
			e = errors.New("[Error] Failed to read file!")
			break
		}
		line := strings.TrimSpace(string(l))

		//empty line
		if len(line) == 0 {
			continue
		}
		// note line
		if string(line[0]) == comment {
			continue
		}

		length := len(line)
		if line[0] == '[' && line[length-1] == ']' {
			section = line[1 : length-1]
			data = make(map[string]Pair)
			data[section] = make(Pair)
		} else{
			// key-value
			i := strings.IndexAny(line, "=")
			if i == -1 {
				fmt.Println("[Error] Incorrect key-value pair!")
				continue
			}
			key := strings.TrimSpace(line[0:i])
			value := strings.TrimSpace(line[i+1 : length])
			// 默认 section
			if section == "" {
				section = "default"
			}
			data = make(map[string]Pair)
			data[section] = make(Pair)
			data[section][key] = value
			c.configuration = append(c.configuration, data)
		}
	}
	return c.configuration, nil
}

// ListenFile 监听文件更改
func ListenFile(infile string) (*Config, error) {
	conf1 := initConfig(infile)
	before, err := conf1.ReadIni()
	if err != nil {
		return nil, err
	}
	var change bool = false
  
	for {
		fmt.Println("--------Listening--------")
		conf2 := initConfig(infile)
		after, err := conf2.ReadIni()
		if err != nil {
			return nil, err
		}
		if len(before) != len(after) {
			c := initConfig(infile)
			c.configuration = after
			fmt.Println("\nThe file has been changed")
			return c, nil
		}
		for _, data := range after {
			for sectionKey, section := range data {
				for key, newVal := range section {
					change = false
					for _, oldMap := range before {
						oldVal := oldMap[sectionKey][key]
						if oldVal == newVal {
							change = true
						}
					}
					if change == false {
						c := initConfig(infile)
						c.configuration = after
						fmt.Println("\nThe file has been changed !")
						return c, nil
					} 
				}
			}
		}
		time.Sleep(1 * time.Second)
	}
}

// Watch 监听配置文件是否被修改
func Watch(filename string, Listener ListenFunc) (*Config, error) {
	Listener = ListenFile
	return Listener.Listen(filename)
}

